<?php
namespace Api\Controller;
use Think\Controller;
use Think\Vender;
class BaseController extends Controller{
	//这里的appid 为弘孝会的小程序账号
	const APPID='wx51e1faf2942a7e1d';
	const SECRET='41beea5d551ef9b15a9a108a82e72bb8';
	const MCH_ID='1490328182';
	const KEY='EaAlMyhV1hPkE433zAOLQkfRbgjPVFwz';			//const用于类成员变量的定义，一经定义，不可修改 可在类内部或者外部定义

	public function pay($openid,$price,$order_no){
		//统一下单接口
		$return=$this->weixinapp($openid,$price,$order_no);
		return $return;
	}

	//生成签名
	public function getSign($arr){		//接受传入的参数 数组格式
		//去除数组中的空值(有key无value的元素)
		$arr=array_filter($arr); 		//数组去除空值array_filter()方法

		if(isset($arr['sign'])){		//判断原始数据是否包含签名，如果包含进行删除。原因：最终签名也会被纳入生成签名里面,最初的签名只需要原始参数	
			unset($arr['sign']);
		}

		ksort($arr);					//对数组的键值进行字典排序(按字母从大到小)

		//生成URL格式的字符串(a=1&b=2$c=3)
		$str=$this->arrToUrl($arr).'&key='.self::KEY; //self在访问PHP类中的成员变量或方法时，如果被引用的变量或者方法被声明成const或者static,那么就必须使用操作符

		//MD5加密 并且转换大写 最终生成加密签名
		return strtoupper(md5($str));			//strtoupper()把所有字符转换为大写	
	}

	//解码中文字符串
	public function arrToUrl($arr){
		//生成URL格式的字符串可能存在中文，需要进行解码
		return urldecode(http_build_query($arr));	//self在访问PHP类中的成员变量或方法时，如果被引用的变量或者方法被声明成const或者static,那么就必须使用操作符
		//urldecode()函数与urlencode()函数原理相反,用于解码已编码的 URL字符串,其原理就是把十六进制字符串转换为中文字符
	}

	//设置签名(获取带签名的数组或不带签名的数组)
	public function setSign($arr){
		//原始传入的数组参数不带签名(还未进行生成),后期进行验证签名的时候需要原始数据及签名，所以要动态的把签名添加进原始数组
		$arr['sign']=$this->getSign($arr);
		return $arr;
	}

	//验证签名
	public function checkSign($arr){
		$sign=$this->getSign($arr);		//getSign()函数最终会返回 原始数据加密后生成的签名字符串
		if($sign == $arr['sign']){		//传进来的$arr是带签名的数组 2个签名进行对比
			return true;
		}else{
			return false;
		}
	}

	//JSAPI统一下单API
	public function unifiedOrder($openid,$price,$order_no){
		$url='https://api.mch.weixin.qq.com/pay/unifiedorder'; //获取预支付号prepay_id 请求地址
		$params=[
			'appid'=>self::APPID,
			'mch_id'=>self::MCH_ID,
			'nonce_str'=>$this->createNoncestr(),
			'body'=>'商城支付测试',
			'out_trade_no'=>$order_no, //商户自定义订单号 一般生产过程由前端传过来
			'total_fee'=>intval($price),		//金额以分为单位，且是整数
			'spbill_create_ip'=>$_SERVER['REMOTE_ADDR'],//终端IP
			'notify_url'=>'https://cgj.xjsyxxkj.com/Api/Nottify/notify',
			'product_id'=>'15699206397',//商品ID trade_type=NATIVE时此参数必传,商户自行定义
			'openid'=>$openid,
			'trade_type'=>'JSAPI'//交易类型
		];
		//把统一下单原始数据生成签名
		$params=$this->setSign($params);
		$xmlparams=$this->arrayToXml($params);	//获取到带签名的数组转换成xml格式
		
		$return=$this->xmlToArray($this->postXmlSSLCurl($xmlparams,$url,60)); //调用curl获取 包含预支付订单信息 prepay_id 的数组
		
		return $return;  //返回数组
	}
	//微信小程序接口
	private function weixinapp($openid,$price,$order_no){
		//统一下单接口
		$unifiedorder=$this->unifiedorder($openid,$price,$order_no);	//获取包含prepay_id 的数组
		//小程序支付数据数据 
		$parameters=array(
			'appId'=>self::APPID,//小程序ID
			'timeStamp'=>''.time().'',//时间戳
			'nonceStr'=>$this->createNoncestr(),//随机串
			'package'=>'prepay_id='.$unifiedorder['prepay_id'],//数据包
			'signType'=>'MD5'//签名方式
		);
		//签名
		$sign=$this->setSign($parameters);
		$parameters['paySign']=$sign['sign'];
		
		return $parameters; //数据返回给发起支付请求的页面 页面接收该参数
	}

	//作用：产生随机字符串，不长于32位
	private function createNoncestr($length = 32 ){
		$chars = "abcdefghijklmnopqrstuvwxyz0123456789";  
		$str ="";
		for ( $i = 0; $i < $length; $i++ )  {  
			$str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);  
		}  
		return $str;
	}
	/** 
     *  作用：将array转为xml
     */ 
	function arrayToXml($arr){  
        $xml = "<xml>";  
        foreach ($arr as $key=>$val)  
        {  
             if (is_numeric($val))  
             {  
                $xml.="<".$key.">".$val."</".$key.">";   
  
             }  
             else  
                $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";    
        }  
        $xml.="</xml>";  
        return $xml;   
    }  
      
    /** 
     *  作用：将xml转为array 
     */  
    public function xmlToArray($xml){         
        //将XML转为array          
        $array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);        
        return $array_data;  
    }


    /** 
     *  作用：以post方式提交xml到对应的接口url 
     */  
    public function postXmlSSLCurl($xml,$url,$second=30)  {         
        //初始化curl          
        $ch = curl_init();  
        //设置超时  
        curl_setopt($ch, CURLOP_TIMEOUT, $second);  
        //这里设置代理，如果有的话  
        //curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');  
        //curl_setopt($ch,CURLOPT_PROXYPORT, 8080);  
        curl_setopt($ch,CURLOPT_URL, $url);  
        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);  //不检测服务器安装证书是否正确
        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);  
        //设置header  
        curl_setopt($ch, CURLOPT_HEADER, FALSE);  //设置为false
        //要求结果为字符串且输出到屏幕上  
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);  
        //post提交方式  
        curl_setopt($ch, CURLOPT_POST, TRUE);  
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);  
        //运行curl  
        $data = curl_exec($ch);  
        curl_close($ch);  
        //返回结果  
        if($data){  
            curl_close($ch);  
            return $data;  
        }  
        else{   
            $error = curl_error($ch);  
            echo "curl出错，错误码:$error"."<br>";     
            curl_close($ch);  
            return false;  
        }  

	}
	/** 
     *  作用：创建日志文件 
     */ 
	public function log($data){
		$years = date('Y-m');
		$url='./Public/logo/h5log'.$years.'/'.time().'stat.txt'; //设置路径
		$dir_name=dirname($url); //dirname() 函数返回路径中的目录部分
		if(!file_exists($dir_name)){ //如果指定的文件或目录存在则返回 true，否则返回 false
			//不存在就创建文件目录
			$res = mkdir(iconv("UTF-8", "GBK", $dir_name),0777,true);//mkdir() 函数创建目录。若成功，则返回 true，否则返回 false。iconv防止中文名乱码
		}
		$fp = fopen($url,"a");//打开文件或者 URL 第二个参数a文件不存在则尝试创建之 这里的创建文件与创建目录不同  
		fwrite($fp,var_export($data,true)); //fwrite写入文件 第一个参数写入的地址，第二个写入的字符串。var_export返回变量的结构信息
		fclose($fp);//fclose() 函数关闭一个打开文件
	}
}